﻿using Blm.Utils.Extensions;
using Blm.Utils.Utility;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using VisionCloud.Core.Model.Ms;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Fms;
using VisionCloud.Domain.DtoModel.Inner;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.DtoModel.Schedule.ProjectInfo;
using VisionCloud.Domain.Options;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.Repository.Interface.Sys;
using VisionCloud.Domain.UnitOfWork;
using VisionCloud.Utility;
using VisionCloud.Utility.Extensions;
using VisionCloud.Utility.Redis;

namespace VisionCloud.Service.Fms
{
    /// <summary>
    /// 项目数据中台API服务
    /// </summary>
    public class ProjectDCService
    {
        public IMsProjectRepository MsProjectRepository { set; get; }

        public ISysDistrictRepository SysDistrictRepository { set; get; }

        public ISysUserDeviceTraceRepository SysUserDeviceTraceRepository { set; get; }

        public IUnitOfWork UnitOfWork { set; get; }

        public ILogger<ProjectDCService> Logger { get; set; }

        /// <summary>
        /// FMS平台网关URL
        /// </summary>
        private readonly string baseUrl;

        /// <summary>
        /// 自定义标识。mrb means mesuring robot
        /// </summary>
        private readonly string userName;

        /// <summary>
        /// FMS平台访问秘钥
        /// </summary>
        private readonly string secretKey;

        /// <summary>
        /// FMS平台网关URL
        /// </summary>
        private readonly string productionBaseUrl;

        /// <summary>
        /// 自定义标识。mrb means mesuring robot
        /// </summary>
        private readonly string productionUserName;

        /// <summary>
        /// FMS平台访问秘钥
        /// </summary>
        private readonly string productionSecretKey;

        /// <summary>
        /// FMS生产域名
        /// </summary>
        private readonly string productionFmsDomain;

        public ProjectDCService(IOptions<FmsGatewayOption> options)
        {
            baseUrl = options.Value.ProjectDCBaseUrl;
            userName = options.Value.ProjectDCUserName;
            secretKey = options.Value.ProjectDCSecret;
            productionBaseUrl = options.Value.ProductionProjectDCBaseUrl;
            productionUserName = options.Value.ProductionProjectDCUserName;
            productionFmsDomain = options.Value.ProductionFmsDomain;
            productionSecretKey = options.Value.ProductionProjectDCSecret;
        }

        /// <summary>
        /// 获取FMS项目的编号
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetProjectDCCode(FindWithFloorRqDto request)
        {
            var project = await MsProjectRepository.FirstAsync(x => x.ProjectId == request.ProjectId.ToGuid());
            if (project == null || project.MapProjectId.IsNullOrWhiteSpace())
                return new ApiResult
                {
                    Code = AjaxCode.Success,
                    Message = "YB0054".SLocalize(),
                    Result = null
                };
            var result = await ListWithBuildingFloors(new FindWithFloorFromDCRqDto
            {
                ProjectCode = project.MapProjectId
            });
            if (result == null || result.Data == null)
                return new ApiResult
                {
                    Code = AjaxCode.Success,
                    Message = "YB0055".SLocalize(),
                    Result = null
                };
            var buildingList = result.Data.FirstOrDefault();
            if (request.BuildingName != null && buildingList.BuildingList.IsNotNull())
                buildingList.BuildingList = buildingList.BuildingList.Where(x => x.BuildingName == request.BuildingName).ToList();
            if (request.FloorName != null && buildingList.BuildingList.IsNotNull())
                buildingList.BuildingList?.ForEach(x =>
                {
                    x.FloorList = x.FloorList.Where(f => f.FloorName == request.FloorName).ToList();
                });
            return new ApiResult
            {
                Code = AjaxCode.Success,
                Message = "YB0056".SLocalize(),
                Result = buildingList
            };
        }

        /// <summary>
        /// 同步没有数据中台映射ID的项目，每次10条
        /// </summary>
        /// <returns></returns>
        public int AsyncDCProjectMapIdData()
        {
            int count = 0;
            try
            {
                // 获取近1个月创建项目
                var thisMonth = DateTime.Now.AddMonths(-1);
                var projectList = MsProjectRepository.Where(
                    x => string.IsNullOrEmpty(x.MapProjectId)
                    && x.CreatedDate >= thisMonth
                    && !x.IsDeleted).Take(100).ToList();
                projectList?.ForEach(project =>
                {
                    string dcProjectId = string.Empty;
                    var dcPro = FindByName(project.Name).GetAwaiter().GetResult();
                    if (dcPro.Data == null)
                    {
                        project.MapProjectId = AddProject(project).GetAwaiter().GetResult();
                        dcPro = FindByName(project.Name).GetAwaiter().GetResult();
                    }
                    if (dcPro.Data == null) return;
                    dcProjectId = dcPro.Data.Id;
                    project.MapProjectId = dcPro.Data.ProjectCode;
                    var detailInfoList = JsonConvert.DeserializeObject<List<Detail>>(project.Details);
                    detailInfoList?.ForEach(x =>
                     {
                         List<AddFloorRqDto> addFloors = new List<AddFloorRqDto>();
                         int floor = 1;
                         while (floor <= x.Floor.ToInt())
                         {
                             addFloors.Add(new AddFloorRqDto
                             {
                                 FloorName = floor.ToString()
                             });
                             floor++;
                         }
                         var addResult = AddBuilding(new AddBuildingRqDto
                         {
                             BuildingName = x.Building,
                             floorList = addFloors,
                             State = 0,
                             ProjectId = dcProjectId
                         }).GetAwaiter().GetResult();
                     });
                    MsProjectRepository.Update(project);
                    count++;
                });
                if (count > 0)
                    UnitOfWork.SaveChangesAsync().GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                Logger.LogError("同步没有数据中台映射ID的项目错误", ex);
                return count;
            }
            return count;
        }

        /// <summary>
        /// 向数据中心添加项目
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public async Task<string> AddProject(MsProject project)
        {
            if (string.IsNullOrEmpty(project.Name)) return string.Empty;
            var dcProject = await FindByName(project.Name);
            ProjectDCRspDto<AddProjectRspDto> addResult;
            if (dcProject != null && !string.IsNullOrWhiteSpace(dcProject.Data?.ProjectCode))
                return dcProject.Data.ProjectCode;

            var districtDto = await this.GetDistrictByProject(project);
            var request = new AddProjectRqDto
            {
                IsPublic = false,
                ProjectName = project.Name,
                AdCode = districtDto.AdCode,
                CityCode = districtDto.CityCode,
                Location = project.Address,
                Center = districtDto.Center,
            };
            // 如腾讯代理服务无定位数据从数据库获取经纬度信息
            if (project.Latitude == 0f || project.Longitude == 0f)
            {
                project.Longitude = double.Parse(districtDto.Center.Split(",")[0]);
                project.Latitude = double.Parse(districtDto.Center.Split(",")[1]);
            }
            addResult = await AddProject(request);
            if (addResult == null || addResult.Code != 0)
                Logger.LogError($"向数据中台添加项目发生错误。数据：{JsonConvert.SerializeObject(request)}。返回信息：{JsonConvert.SerializeObject(addResult)}");
            return addResult?.Data?.ProjectCode;
        }

        /// <summary>
        /// 根据项目信息获取行政区域代码
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public async Task<DistrictDto> GetDistrictByProject(MsProject project)
        {
            #region 校验省-市-区

            if (string.IsNullOrWhiteSpace(project.Province))
            {
                throw new OperationException("省份信息不能为空");
            }

            if (string.IsNullOrWhiteSpace(project.City))
            {
                throw new OperationException("市区信息不能为空");
            }

            if (string.IsNullOrWhiteSpace(project.Town))
            {
                throw new OperationException("镇区信息不能为空");
            }

            if (string.IsNullOrWhiteSpace(project.Address))
            {
                throw new OperationException("详细地址信息不能为空");
            }

            #endregion 校验省-市-区

            // 将Fms行政区域信息缓存
            var districtInfo = await RedisHelper.Instance.GetAsync<List<DistrictDto>>("Web:Sys:district");
            if (districtInfo == null)
            {
                var result = SysDistrictRepository.GetAll().Select(x => new DistrictDto
                {
                    Id = x.Id,
                    ParentId = x.ParentId,
                    Name = x.Name,
                    PinyinHeadChar = x.PinyinHeadChar,
                    CityCode = x.CityCode,
                    AdCode = x.AdCode,
                    Level = (int)x.Level,
                    Center = x.Center,
                    PolyLine = x.PolyLine,
                }).ToList();
                if (result != null && result.Count != 0)
                {
                    districtInfo = result;
                    await RedisHelper.Instance.SetAsync<List<DistrictDto>>("Web:Sys:district", result, TimeSpan.FromDays(7));
                }
            }
            // 获取省份信息
            var proviceInfo = districtInfo?.Where(x => x.Name.Contains(project.Province) && x.Level == 1).FirstOrDefault();
            if (proviceInfo == null)
            {
                Logger.LogError($"省份信息有误{project.ToJsonStr()}");
                throw new OperationException("省份信息有误");
            }

            // 获取省份下城市信息
            var cityInfo = districtInfo?.Where(x => x.Name.Contains(project.City) && x.Level == 2 && x.ParentId == proviceInfo.Id).FirstOrDefault();
            if (cityInfo == null)
            {
                Logger.LogInformation($"无城市信息{project.ToJsonStr()}");
                return proviceInfo;
            }

            // 获取城市下的区县信息
            var townInfo = districtInfo?.Where(x => x.Name.Contains(project.Town) && x.Level == 3 && x.ParentId == cityInfo.Id).FirstOrDefault();
            if (townInfo == null)
            {
                Logger.LogInformation($"无区县信息{project.ToJsonStr()}");
                return cityInfo;
            }

            return townInfo;
        }

        /// <summary>
        /// 添加楼栋，同时向数据中台添加楼栋信息。
        /// </summary>
        /// <param name="project"></param>
        /// <returns></returns>
        public async Task<bool> AddBuilding(MsProject project)
        {
            try
            {
                bool result = true;
                if (string.IsNullOrWhiteSpace(project.MapProjectId))
                {
                    Utility.Logger.Info("FMS返回的项目Id为空");
                    return false;
                }
                var fmsResult = await FindByCode(project.MapProjectId);
                if (fmsResult == null || fmsResult.Data == null || string.IsNullOrWhiteSpace(fmsResult.Data.Id))
                {
                    Utility.Logger.Info("FMS返回的结果为空");
                    return false;
                }
                var detailInfoList = JsonConvert.DeserializeObject<List<Detail>>(project.Details);
                var unPublishedList = detailInfoList.GetRange(
                    project.PubliceDetailsCount, detailInfoList.Count - project.PubliceDetailsCount);
                unPublishedList?.ForEach(async x =>
                {
                    List<AddFloorRqDto> addFloors = new List<AddFloorRqDto>();
                    int floor = 1;
                    while (floor <= x.Floor.ToInt())
                    {
                        addFloors.Add(new AddFloorRqDto
                        {
                            FloorName = floor.ToString()
                        });
                        floor++;
                    }
                    var addResult = await AddBuilding(new AddBuildingRqDto
                    {
                        BuildingName = x.Building,
                        floorList = addFloors,
                        State = 0,
                        ProjectId = fmsResult.Data.Id
                    });
                    result = addResult != null && addResult.Code == 0;
                });
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"添加楼栋楼层错误", ex);
                return false;
            }
        }

        /// <summary>
        /// 查询带对应楼栋楼层列表的项目列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ProjectDCListRspDto<FindWithFloorFromDCRspDto>> ListWithBuildingFloors(FindWithFloorFromDCRqDto request)
        {
            string body = JsonConvert.SerializeObject(request);
            string api = productionFmsDomain + $"dc/v1/project/listWithBuildingFloors?from={userName}";

            return await ListWithBuildingFloors(api, userName, secretKey, body);
        }

        /// <summary>
        /// 查询全部带对应楼栋楼层列表的项目列表（此方法只读取生产环境的数据）
        /// </summary>
        /// <returns></returns>
        public async Task<ProjectDCListRspDto<FindWithFloorFromDCRspDto>> ListWithBuildingFloorsAll()
        {
            string body = JsonConvert.SerializeObject(new { });
            string api = productionFmsDomain + $"dc/v1/project/listWithBuildingFloors";

            return await ListWithBuildingFloors(api, productionUserName, productionSecretKey, body);
        }

        /// <summary>
        /// 查询带对应楼栋楼层列表的项目列表
        /// </summary>
        /// <param name="api"></param>
        /// <param name="username"></param>
        /// <param name="secret"></param>
        /// <param name="body">body内容</param>
        /// <returns></returns>
        private async Task<ProjectDCListRspDto<FindWithFloorFromDCRspDto>> ListWithBuildingFloors(string api, string username, string secret, string body)
        {
            var headers = new Dictionary<string, string>();
            string responseStr = string.Empty;
            headers.Add("source", username);
            BuildAuth(headers, api.Split("com")[1], username, secret, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 60, headers))
            {
                try
                {
                    responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCListRspDto<FindWithFloorFromDCRspDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return new ProjectDCListRspDto<FindWithFloorFromDCRspDto>();
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误，请求参数{body}，响应结果{responseStr}", ex);
                    return new ProjectDCListRspDto<FindWithFloorFromDCRspDto>();
                }
            }
        }

        /// <summary>
        /// 根据项目名称精确查询项目信息
        /// </summary>
        /// <param name="projectName"></param>
        /// <returns></returns>
        public async Task<ProjectDCRspDto<ProjectDCFindProjectDto>> FindByName(string projectName)
        {
            string requestUrl = $"dc/v1/project/findByName/{projectName}?from={userName}";
            var api = baseUrl + requestUrl;
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "GET", null);
            using (var response = await HttpUtility.GetAsync(api, headers, 60))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<ProjectDCFindProjectDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return new ProjectDCRspDto<ProjectDCFindProjectDto>();
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return new ProjectDCRspDto<ProjectDCFindProjectDto>();
                }
            }
        }

        /// <summary>
        /// 根据项目编码精确查询项目信息
        /// </summary>
        /// <param name="projectCode"></param>
        /// <returns></returns>
        public async Task<ProjectDCRspDto<ProjectDCFindProjectDto>> FindByCode(string projectCode)
        {
            string requestUrl = $"dc/v1/project/findByCode/{projectCode}?from={userName}";
            var api = baseUrl + requestUrl;
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "GET", null);
            using (var response = await HttpUtility.GetAsync(api, headers, 60))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<ProjectDCFindProjectDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}{api}");
                        return null;
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 新增项目并保持映射
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ProjectDCRspDto<AddProjectRspDto>> AddProjectWithMapping(AddProjectRqDto request)
        {
            string requestUrl = $"dc/v1/project/addWithMapping?from={userName}";
            var api = baseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(request);
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 60, headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<AddProjectRspDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return null;
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 新增项目
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ProjectDCRspDto<AddProjectRspDto>> AddProject(AddProjectRqDto request)
        {
            string requestUrl = $"dc/v1/project/add?from={userName}";
            var api = baseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(request);
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 60, headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<AddProjectRspDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return null;
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 查询FMS城市行政区划列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ProjectDCListRspDto<DistrictDto>> GetListDistrict()
        {
            string requestUrl = $"dc/v1/district/listAll?from={userName}";
            var api = baseUrl + requestUrl;
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "GET", null);
            using (var response = await HttpUtility.GetAsync(api, headers, 60))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCListRspDto<DistrictDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return null;
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 新增楼栋楼层
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<ProjectDCRspDto<AddBuildingRspDto>> AddBuilding(AddBuildingRqDto request)
        {
            string requestUrl = $"dc/v1/building/addWithFloors?from={userName}";
            var api = baseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(request);
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 30, headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<AddBuildingRspDto>>(responseStr);
                    if (result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return null;
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 推送设备的累计测量面积和累计作业时长
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsResponseDto> PushDeviceStatistics(DevicePushFMSDto request)
        {
            FmsResponseDto result = null;
            string requestUrl = "v1/robot/pushMeasureStatistics";
            string body = JsonConvert.SerializeObject(request);
            return await PostFmsAsync(requestUrl, body);
        }

        /// <summary>
        /// 把工单的测量结果反馈到FMS系统
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsResponseDto> FreeBackWordOrder(WorkOrderFreebackDto request)
        {
            string requestUrl = "to/v1/task/mrb/order/feedback";
            //var api = baseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(request);
            return await PostFmsAsync(requestUrl, body);
        }

        /// <summary>
        /// 从FMS获取项目详细信息（带出楼栋楼层详情）
        /// </summary>
        /// <param name="fmsProjectCode"></param>
        /// <returns></returns>
        public async Task<ProjectSyncFmsDto> GetFmsProjectInfos(string fmsProjectCode)
        {
            string requestUrl = $"dc/v1/project/getDetailByCode?from=fms&projectCode={fmsProjectCode}";
            var api = productionBaseUrl + requestUrl;
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], productionUserName, productionSecretKey, "POST");
            using (var response = await HttpUtility.PostAsync(api, headers: headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    var result = JsonConvert.DeserializeObject<ProjectDCRspDto<ProjectSyncFmsDto>>(responseStr);
                    if (result == null || result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        return null;
                    }
                    return result.Data;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 获取项目的天花模型json文件（此方法只读取生产环境的数据）
        /// </summary>
        /// <param name="projectCode"></param>
        /// <param name="buildingCode"></param>
        /// <param name="floorCode"></param>
        /// <returns></returns>
        public async Task<ListSpaceCeilingResponseDto> ListSpaceCeiling(string projectCode, string buildingCode, string floorCode)
        {
            string requestUrl = "wdc/v1/ceilingPolish/listSpaceCeiling";
            var api = productionBaseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(new { bimMapCode = $"{projectCode}-{buildingCode}-{floorCode}", version = "1.0" });
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], productionUserName, productionSecretKey, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 30, headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    if (string.IsNullOrEmpty(responseStr))
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        throw new ArgumentNullException("Http Content内容为空");
                    }
                    var result = JsonConvert.DeserializeObject<ListSpaceCeilingResponseDto>(responseStr);
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 获取项目的内墙模型json文件（此方法只读取生产环境的数据）
        /// </summary>
        /// <param name="projectCode"></param>
        /// <param name="buildingCode"></param>
        /// <param name="floorCode"></param>
        /// <returns></returns>
        public async Task<ListSpaceCeilingResponseDto> ListSpaceInnerWall(string projectCode, string buildingCode, string floorCode)
        {
            string requestUrl = "wdc/v1/innerWall/listSpace";
            var api = productionBaseUrl + requestUrl;
            string body = JsonConvert.SerializeObject(new { bimMapCode = $"{projectCode}-{buildingCode}-{floorCode}", version = "1.0" });
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], productionUserName, productionSecretKey, "POST", body);
            using (var response = await HttpUtility.PostAsync(api, body, 30, headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    if (string.IsNullOrEmpty(responseStr))
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        throw new ArgumentNullException("Http Content内容为空");
                    }
                    var result = JsonConvert.DeserializeObject<ListSpaceCeilingResponseDto>(responseStr);
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 获取门线模型数据
        /// </summary>
        /// <param name="projectCode"></param>
        /// <param name="buildingCode"></param>
        /// <param name="floorCode"></param>
        /// <returns></returns>
        public async Task<string> FindSpaceUnit(string projectCode, string buildingCode, string floorCode)
        {
            //var api = @"https://fms.bzlrobot.com/map/v1/spaceUnit/find";

            string requestUrl = "map/v1/spaceUnit/find";
            var api = productionFmsDomain + requestUrl;
            var url = $@"{api}/{projectCode}-{buildingCode}-{floorCode}";
            var headers = new Dictionary<string, string>();
            headers.Add("source", "fms");
            // FMS需求接口鉴权，后期更新生产环境
            BuildAuth(headers, api.Split("com")[1], productionUserName, productionSecretKey, "POST");
            using (var response = await HttpUtility.PostAsync(url, timeout: 30, headers: headers))
            {
                try
                {
                    var responseStr = await response.Content.ReadAsStringAsync();
                    if (string.IsNullOrEmpty(responseStr))
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        throw new ArgumentNullException("Http Content内容为空");
                    }
                    var result = responseStr;
                    return result;
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                    return null;
                }
            }
        }

        /// <summary>
        /// 保存天花整层房间设置
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsResponseDto> SaveFloorRoomSettings(SaveFloorRoomSettingsRqDto request)
        {
            string api = "wdc/v1/ceilingPolish/saveFloorRoomSettings";
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsResponseDto>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        /// <summary>
        /// 查询算法上传的版本列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsAlgorithmApiRspDto<WdcListVersionRspDto>> ListAlgorithmResultVersion(ListAlgorithmRqDto request)
        {
            if (request == null) return null;
            var api = productionBaseUrl + $"wdc/v1/{request.AlgorithmType}/listVersion/{request.BimMapCode}";
            try
            {
                var rsp = await HttpUtility.GetAsync(api);
                var responseStr = await rsp.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<WdcListVersionRspDto>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        public async Task<FmsAlgorithmApiRspDto<SaveCeilingFloorRqDto>> CeilingPolishListSpaceCeiling(WdcBaseRqDto request)
        {
            if (request == null) return null;
            var api = "wdc/v1/ceilingPolish/listSpaceCeiling";
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<SaveCeilingFloorRqDto>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        /// <summary>
        ///上传内墙数据接口 1.2
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsAlgorithmApiRspDto<string>> SaveInnerWallVisionCollectV1_2(SaveInnerWallVisionCollectRqDto request)
        {
            if (request == null) return null;
            var api = "wdc/v1/wall/saveVisionCollects";
            request.Version = "V" + request.Version;
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<string>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        /// <summary>
        ///上传内墙数据接口 1.3
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsAlgorithmApiRspDto<string>> SaveInnerWallVisionCollectV1_3(SaveInnerWallVisionCollectRqDto request)
        {
            if (request == null) return null;
            var api = "wdc/v1/innerWall/saveVisionCollects";
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<string>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        /// <summary>
        /// 上传螺杆洞数据接口
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<FmsAlgorithmApiRspDto<string>> SaveScrewHoleVisionCollect(SaveSrowHoleVisionCollectRqDto request)
        {
            if (request == null) return null;
            var api = "wdc/v1/screwHole/saveVisionCollects";
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<string>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        public async Task<FmsAlgorithmApiRspDto<SaveCeilingFloorRspDto>> SaveCeilingPolishFloor(SaveCeilingFloorRqDto request)
        {
            if (request == null) return null;
            var api = "wdc/v1/ceilingPolish/saveFloor";
            try
            {
                var responseStr = await WdcApiPostAsync(api, request);
                var result = JsonConvert.DeserializeObject<FmsAlgorithmApiRspDto<SaveCeilingFloorRspDto>>(responseStr);
                return result;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        public async Task<FmsResponseDto> PushDeviceLocation(DateTime start, DateTime end)
        {
            var list = new DeviceLocationsDto { Data = await SysUserDeviceTraceRepository.WhereAsNoTrackingAsync(x => x.CreatedDate >= start && x.CreatedDate <= end) };

            string requestUrl = "v1/robot/pushMeasureLocation";
            var serializerSettings = new JsonSerializerSettings
            {
                // 设置为小驼峰命名
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            };
            string body = JsonConvert.SerializeObject(list, Formatting.None, serializerSettings);

            return await PostFmsAsync(requestUrl, body);
        }

        #region 私有方法

        private async Task<string> WdcApiPostAsync(string api, WdcBaseRqDto request)
        {
            api = productionBaseUrl + api;
            string body = JsonConvert.SerializeObject(request);
            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], productionUserName, productionSecretKey, "POST", body);
            HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(300) };
            if (headers == null) headers = new Dictionary<string, string>();
            if (headers != null && headers.Count != 0)
            {
                foreach (var item in headers)
                {
                    client.DefaultRequestHeaders.Add(item.Key, item.Value);
                }
            }
            var buffer = Encoding.UTF8.GetBytes(body);
            using var byteContent = new ByteArrayContent(buffer);
            byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var response = await client.PostAsync(api, byteContent);
            try
            {
                var responseStr = await response.Content.ReadAsStringAsync();
                Utility.Logger.Info($"WdcApi 接口：{api},返回：{responseStr}[{response.StatusCode}]");
                if (string.IsNullOrEmpty(responseStr))
                {
                    throw new ArgumentNullException("Http Content内容为空");
                }
                return responseStr;
            }
            catch (Exception ex)
            {
                Utility.Logger.Error($"请求{api}错误！", ex);
                return null;
            }
        }

        /// <summary>
        /// Post请求fms
        /// </summary>
        /// <param name="requestUrl">请求接口url</param>
        /// <param name="bodyStr">已序列化的请求体</param>
        /// <returns>请求fms结果 <see cref="FmsResponseDto"/></returns>
        private async Task<FmsResponseDto> PostFmsAsync(string requestUrl, string bodyStr)
        {
            FmsResponseDto result = null;
            var api = baseUrl + requestUrl;

            var headers = new Dictionary<string, string>();
            BuildAuth(headers, api.Split("com")[1], userName, secretKey, "POST", bodyStr);
            using (var response = await HttpUtility.PostAsync(api, bodyStr, 30, headers))
            {
                try
                {
                    if (response == null)
                    {
                        throw new ArgumentNullException("HttpResponseMessage为空");
                    }
                    var responseStr = await response.Content.ReadAsStringAsync();
                    Utility.Logger.Info($"请求FMS系统接口记录，Api：{api}，请求体：{bodyStr}，返回内容：{responseStr}");
                    if (string.IsNullOrEmpty(responseStr))
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                        throw new ArgumentNullException("Http Content内容为空");
                    }

                    result = JsonConvert.DeserializeObject<FmsResponseDto>(responseStr);
                    if (result == null || result.Code != 0)
                    {
                        Utility.Logger.Info($"request code:{responseStr}[{response.StatusCode}]");
                    }
                }
                catch (Exception ex)
                {
                    Utility.Logger.Error($"请求{api}错误！", ex);
                }
            }

            return result;
        }

        #endregion 私有方法

        #region 签名方法

        private void BuildAuth(Dictionary<string, string> headers, string url, string username, string secret, string requestType = "GET", string body = null)
        {
            var baseUrl = UrlEncode(url);
            string requestLine = $"{requestType} {baseUrl} HTTP/1.1";
            string dateNow = DateTime.Now.ToUniversalTime().ToString("r");
            string header = "date request-line";
            string content = $"date: {dateNow}\n{requestLine}";
            if (body != null)
            {
                string bodyAlgorithm = "SHA-256";
                header += " digest";
                string digest = bodyAlgorithm + "=" + GetSHA256Str(body);
                headers.Add("digest", digest);
                content += "\ndigest: " + digest;
            }
            string signature = GetHmacSHA256Str(content, secret);
            string algorithm = "hmac-sha256";
            string auth = $"hmac username=\"{username}\", algorithm=\"{algorithm}\", headers=\"{header}\", signature=\"{signature}\"";

            headers.Add("date", dateNow);
            headers.Add("authorization", auth);
        }

        private static string UrlEncode(string str)
        {
            StringBuilder builder = new StringBuilder();
            foreach (char c in str)
            {
                if (System.Web.HttpUtility.UrlPathEncode(c.ToString()).Length > 1)
                {
                    builder.Append(System.Web.HttpUtility.UrlPathEncode(c.ToString()).ToUpper());
                }
                else
                {
                    builder.Append(c);
                }
            }
            return builder.ToString();
        }

        private static string GetSHA256Str(string content)
        {
            string signRet = string.Empty;
            using (SHA256Managed mac = new SHA256Managed())
            {
                byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(content));
                signRet = Convert.ToBase64String(hash);
            }
            return signRet;
        }

        private static string GetHmacSHA256Str(string content, string secret)
        {
            string signRet = string.Empty;
            using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
            {
                byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(content));
                signRet = Convert.ToBase64String(hash);
            }
            return signRet;
        }

        #endregion 签名方法
    }
}